#include "InterOpReaderHelper.h"
#include "SPAIopEntity.h"
#include "SPAIopWString.h"
#include "common/logger.h"
#include "body.hxx"
#include "cone.hxx"
#include "edge.hxx"
#include "entity.hxx"
#include "lists.hxx"
#include "kernapi.hxx"
#include "lump.hxx"
#include "face.hxx"
#include "surface.hxx"
#include "surdef.hxx"
#include "exchg_funcs.hxx"
#include "subshell.hxx"
#include "torus.hxx"
#include "mmgr.hxx"
#include "shell.hxx"
#include "topology/TopoBuilder.hpp"
#include "topology/TopoSolid.hpp"
#include "topology/TopoShell.hpp"
#include "topology/TopoWire.hpp"
#include "../AMCAXUtils.h"
#include "ReaderTopoConvertHelper.h"
#include "healing/ShapeFixTool.hpp"
#include <codecvt>

#include <healing/ShapeAnalysisFreeBounds.hpp>
#include <occtio/OCCTTool.hpp>
#include <string>
#include <topology/TopoCast.hpp>
#include <topology/TopoEdge.hpp>
#include <topology/TopoExplorer.hpp>
#include <topology/TopologyMacros.hpp>
#include <vector>
#include "utils/SystemUtils.h"
#include "wire.hxx"
#include "loop.hxx"
#include "at_int.hxx"
#include "ga_api.hxx"
#include "collection.hxx"
#include "InterOpAttributeHelper.h"
#include "topology/WireExplorer.hpp"
#include "SPAIopAcisEntity.h"
#ifndef CXX_LESS_17

    #include <filesystem>

#endif

AMCAX_EXCHANGE_NAMESPACE_BEGIN

namespace InterOp::ReaderConvert {

void ConvertLump(ENTITY *entity, AMCAX::TopoShape &parentShape, std::unordered_map<AMCAX::TopoShape, InterOpMaterial> &materials) ;
void ConvertShell(ENTITY *entity, AMCAX::TopoShape &parentShape, std::unordered_map<AMCAX::TopoShape, InterOpMaterial> &materials) ;
void ConvertFace(ENTITY *entity, AMCAX::TopoShape &parentShape, std::unordered_map<AMCAX::TopoShape, InterOpMaterial> &materials) ;
void ConvertWire(ENTITY *entity, AMCAX::TopoShape &parentShape, std::unordered_map<AMCAX::TopoShape, InterOpMaterial> &materials) ;
void ConvertSubshell(ENTITY *entity, AMCAX::TopoShape &parentShape, std::unordered_map<AMCAX::TopoShape, InterOpMaterial> &materials) ;
void ConvertLoop(ENTITY *entity, std::vector<AMCAX::TopoShape> &parentShape, std::unordered_map<AMCAX::TopoShape, InterOpMaterial> &materials,bool faceSense = false) ;
void ConvertCoedge(ENTITY *entity, AMCAX::TopoShape &parentShape, std::unordered_map<AMCAX::TopoShape, InterOpMaterial> &materials,bool face_sense = false);
void ConvertEdge(ENTITY *entity, AMCAX::TopoShape &parentShape, std::unordered_map<AMCAX::TopoShape, InterOpMaterial> &materials,bool coedge_reverse = false) ;

#ifdef DEBUG_READ_FACE_ENABLE
static int faceId = 1 ;
#endif


std::string wstring_to_utf8(const std::wstring& wstr) {
    std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;

    return converter.to_bytes(wstr);
}
void PrintPersistentId(ENTITY * e){
    SPAIopAcisEntity acisIopEntity( e );
    SPAIopWString PIDW = acisIopEntity.GetPID();
    const wchar_t* pPIDWC = NULL;
    PIDW.ToWChar(pPIDWC);
    
    // LOG_DEBUG( "BRep_Entity Persistent ID = {} \n", wstring_to_utf8(pPIDWC));
    // tag_id_type pid  ;
    // api_get_entity_id(e,pid) ;
    // // LOGGER_DEBUG( "BRep_Entity Persistent ID = {} \n", wstring_to_utf8(pPIDWC));
    // LOG_DEBUG( "BRep_Entity Persistent ID = {} \n",pid);
}


void FilterPeriodicFace(ENTITY *entity,ENTITY_LIST &faces, bool isSplitU)
{
    ENTITY_LIST splitFaces;
    FACE *splitFace = nullptr;
             
    ENTITY *e =NULL;
    api_down_copy_entity(entity, e);
    FACE *face = static_cast<FACE *>(e);

    const surface * srf = &(face->geometry()->equation());
    if(isSplitU&&srf->periodic_u())
    {
        api_split_face(face, true, true, 0);
        api_split_face(face, true, true, 0.5);
        splitFace = face;
    }

    if(!isSplitU&&srf->periodic_v())
    {
        api_split_face(face, false, true, 0);
        api_split_face(face, false, true, 0.5);
        splitFace = face;
    }
   
    while(splitFace!=NULL)
    {
        splitFaces.add(splitFace);
        splitFace =splitFace->next();
    }

    if(splitFaces.count()>0){
       faces.add(splitFaces);
    }else{
        faces.add(entity);
    }
}

void ConvertBody(ENTITY *entity, AMCAX::TopoShape &parentShape, std::unordered_map<AMCAX::TopoShape, InterOpMaterial> &materials)
{
    if (!entity)
    {
        LOG_ERROR("entity is null");
        return;
    }          
    if (!is_BODY(entity))
    {
        LOG_WARN("entity is not body");
        return;
    }

    PrintPersistentId(entity); 

    ENTITY_LIST childList;    
    api_get_lumps(entity, childList);
    #ifdef DEBUG_READ_FACE
    faceId = 1 ;
    #endif
    for (auto c : childList)
    {
        PrintPersistentId(c); 
        ConvertLump(c, parentShape, materials);
    }

}

void ConvertLump(ENTITY *entity, AMCAX::TopoShape &parentShape, std::unordered_map<AMCAX::TopoShape, InterOpMaterial> &materials) 
{
    PrintPersistentId(entity); 
    if (!entity)
    {
        LOG_ERROR("entity is null");
        return;
    }
    if (!is_LUMP(entity))
    {
        LOG_WARN("entity is ont lump");
        return;
    }

    ENTITY_LIST childList;
    api_get_shells(entity, childList);
    for (auto c : childList)
    {
        PrintPersistentId(c); 
        ConvertShell(c, parentShape, materials);
    }
}

void ConvertShell(ENTITY *entity, AMCAX::TopoShape &parentShape, std::unordered_map<AMCAX::TopoShape, InterOpMaterial> &materials)
{
    PrintPersistentId(entity); 
    if (!entity)
    {
        LOG_ERROR("entity is null");
        return;
    }
    if (!is_SHELL(entity))
    {
        LOG_WARN("entity is not shell");
        return;
    }
    
    SHELL *shell = dynamic_cast<SHELL *>(entity);
    if (!shell)
    {
        LOG_ERROR("entity is shell");
        return;
    }
    
    AMCAX::TopoBuilder TB;
    ENTITY_LIST childList;
    if (shell->face())
    {
        AMCAX::TopoSolid solidShape;
        TB.MakeSolid(solidShape);

        AMCAX::TopoShell shellShape;
        TB.MakeShell(shellShape);

        api_get_faces(shell, childList);

        ENTITY_LIST filterFacesU;        

        for(auto c:childList){
            PrintPersistentId(c); 
            FilterPeriodicFace(c,filterFacesU,true);
        }

        ENTITY_LIST filterFacesUv;
        for(auto c:filterFacesU){
            FilterPeriodicFace(c,filterFacesUv,false);
        }
        
        for (auto c : filterFacesUv)
        // for (auto c : childList)
        {
            ConvertFace(c, shellShape, materials);              
        }
        AMCAXTopoTools::CombineShapes(solidShape, shellShape);

        // InterOpMaterial shell_mat;
        // parse_material(shell, shell_mat);
       
        AMCAXTopoTools::CombineShapes(parentShape, shellShape);
        // materials.emplace(shellShape, shell_mat);
    }
    else if (shell->wire())
    {
        api_get_wires(shell, childList);
        for (auto c : childList)
        {
            AMCAX::TopoWire wireShape;
            TB.MakeWire(wireShape);
            ConvertWire(c, wireShape, materials);
            if (!wireShape.IsNull())
            {
                AMCAXTopoTools::CombineShapes(parentShape, wireShape);
            }
        }
    }
    else if (shell->subshell())
    {
        for(SUBSHELL *subshell = shell->subshell(); subshell!=NULL; subshell = subshell->sibling())
        {
            ConvertSubshell(subshell, parentShape, materials);
        }
    }

}


void ConvertFace(ENTITY *entity, AMCAX::TopoShape &parentShape, std::unordered_map<AMCAX::TopoShape, InterOpMaterial> &materials) 
{
    PrintPersistentId(entity); 
    if (!entity)
    {
        LOG_ERROR("entity is null");
        return;
    }
    if (!is_FACE(entity))
    {
        LOG_WARN("entity is not face");
        return;
    }
    
    #ifdef DEBUG_READ_FACE_ENABLE

    std::filesystem::path dataFolder = "";
    std::string envFolder = SystemUtil::GetEnv("DATA_DIR") ;
    if (!envFolder.empty()) {
        dataFolder = envFolder ;
    }
    dataFolder /= "debug" ;
    if (!std::filesystem::exists(dataFolder)) {
        std::filesystem::create_directories(dataFolder); 
    }

    std::string originFacePath = (dataFolder / (std::to_string(faceId) + "origin_face.brep")).string() ;
    std::string combineFacePath = (dataFolder / (std::to_string(faceId) + "combine_face.brep")).string() ;
    std::string originWirePath = (dataFolder / (std::to_string(faceId) + "origin_wire.brep")).string() ;
    std::string compoundPath = (dataFolder / (std::to_string(faceId) + "compound.brep")).string() ;
    std::string finalFacePath = (dataFolder / (std::to_string(faceId) + "final_face.brep")).string() ;

    #endif

    FACE *face = static_cast<FACE *>(entity);
    if (!face) {
        LOG_ERROR("entity is not face");
        return;
    }
    SURFACE *surface = face->geometry();
    if (!surface) {
        LOG_ERROR("face geometry is null");
        return;
    }
    bool faceSense=face->sense()==REVERSED;
    bool isCone = is_CONE(surface);
    bool isCylinder = false ;
    if (isCone) {
        isCylinder = (0.0 == ((CONE*)(surface))->sine_angle()) ;
    }
    bool isTorus = is_TORUS(surface);
    // static int count = 0;
    // count++;
    // int testCount = 2118;
    // std::string testPath = "D:\\amcax_dev\\modelFiles\\test\\debug\\brep\\";
    // if(count == testCount)
    // {
    //     int a=0;
    // }
    
    AMCAX::TopoFace faceShape = ReaderTopoConvertHelper::ConvertFace(face);

    if (faceShape.IsNull()) {
        return ;
    }
    // faceShape.SetOrientation(faceSense?OrientationType::Forward:OrientationType::Reversed) ;
    
    #ifdef DEBUG_READ_FACE_ENABLE

    AMCAX::OCCTIO::OCCTTool::Write(faceShape,originFacePath,false);

    #endif

    ENTITY_LIST childList;
    api_get_loops(entity, childList);      

    AMCAX::TopoBuilder TB;
    if (childList.count() <= 2 && isCone && !isCylinder) 
    {

        // faceShape = INTEROP::GEOM_EXCHANGE::make_amcax_face(face);
        // AMCAX::TopoWire wire_shape;
        // TB.MakeWire(wire_shape);
        std::vector<AMCAX::TopoShape> wire_shapes;
        ConvertLoop(childList[0], wire_shapes, materials,isTorus?faceSense:false);
        for(auto wire_shape : wire_shapes)
        {
            AMCAXTopoTools::CombineShapes(faceShape, wire_shape);
        }

        #ifdef DEBUG_READ_FACE_ENABLE
        
        faceId++ ;

        #endif

        AMCAXTopoTools::CombineShapes(parentShape, faceShape, true);
        return;
    }


    for (auto c : childList)
    {
        // AMCAX::TopoWire wireShape;
        // TB.MakeWire(wireShape);
        std::vector<AMCAX::TopoShape> wireShapes;
        ConvertLoop(c, wireShapes, materials,isTorus?faceSense:false);
      
        // wireShape.SetOrientation(OrientationType::Forward) ;
        // if (!faceSense) {
        //     wireShape.SetOrientation(OrientationType::Reversed) ;
        // }
        // wireShape.Reverse() ;
        
        // if(count == 13 || count == 14)
        // { 
            
        //     ENTITY_LIST acisEntitys;
        //     acisEntitys.add(c);
        //     InterOp::Writer wr;
        //     wr.WriterToFile(testPath+std::to_string(count)+"ACISLOOP.Sat",acisEntitys);

        #ifdef DEBUG_READ_FACE_ENABLE

        // AMCAX::OCCTIO::OCCTTool::Write(wireShape,originWirePath,false);

        #endif
        int index = 0 ;
        for(auto wireShape : wireShapes)
        {
            if (!wireShape.Closed()) {
                continue;
            }
            if (!wireShape.IsNull())
            {
                #ifdef DEBUG_READ_FACE_ENABLE
                
                originWirePath = (dataFolder / (std::to_string(faceId)+ "_" + std::to_string(index++) + "origin_wire.brep")).string() ;
                AMCAX::OCCTIO::OCCTTool::Write(wireShape,originWirePath,false);
                
                #endif
                // if(count == testCount)
                // {
                //     static int wireId = 0;
                //     wireId++;
                //     // if(wireId == 1)
                //     // {
                //     //     continue;
                //     // }
                //     std::string originWirePath = testPath+std::to_string(wireId) + "origin_wire.brep";
                //     AMCAX::OCCTIO::OCCTTool::Write(wireShape,originWirePath,false);
                // }
                if(!isTorus){
                    wireShape.SetOrientation(faceSense?OrientationType::Reversed:OrientationType::Forward) ;
                }
                #ifdef DEBUG_READ_FACE_ENABLE
    
                // TopoCompound comp;
                // TB.MakeCompound(comp) ;
                // TB.Add(comp,wireShape) ;
                // TB.Add(comp,faceShape) ;
                // AMCAX::OCCTIO::OCCTTool::Write(comp,compoundPath,false);
    
                #endif
                
                AMCAXTopoTools::CombineShapes(faceShape, wireShape,false);        
                
    
                #ifdef DEBUG_READ_FACE_ENABLE
    
                AMCAX::OCCTIO::OCCTTool::Write(faceShape,combineFacePath,false);
                
    
                #endif
            }
        }
  
    }

   
    InterOpMaterial faceMat;
    //if(count == testCount) AMCAX::OCCTIO::OCCTTool::Write(faceShape,testPath+std::to_string(count)+"Original_Face.brep",false);//结果错误
    auto fixShape=AMCAX::ShapeFixTool::FixShape(faceShape, 1e-4, 1e-2);
    //if(count == testCount) AMCAX::OCCTIO::OCCTTool::Write(faceShape,testPath+std::to_string(count)+"Modifyed_Face.brep",false);//结果正确
    //if(count == testCount) AMCAX::OCCTIO::OCCTTool::Write(fixShape,testPath+std::to_string(count)+"Fixed_Face.brep",false);//结果错误
    AMCAXTopoTools::CombineShapes(parentShape, fixShape);  
    
   
    // materials.insert(std::make_pair(faceShape, faceMat));

    #ifdef DEBUG_READ_FACE_ENABLE

    AMCAX::OCCTIO::OCCTTool::Write(fixShape,finalFacePath,false);
    
    faceId++ ;

    #endif
}

void ConvertWire(ENTITY *entity, AMCAX::TopoShape &parentShape, std::unordered_map<AMCAX::TopoShape, InterOpMaterial> &materials)
{
    PrintPersistentId(entity); 
    if (!entity)
    {
        LOG_ERROR("entity is null");
        return;
    }
    if (!is_WIRE(entity))
    {
        LOG_WARN("entity is not wire");
        return;
    }

    ENTITY_LIST child_list;
    api_get_coedges(entity, child_list);
    std::vector<AMCAX::TopoShape> edges;
    std::vector<AMCAX::TopoShape> wires;
    for (auto c : child_list)
    {
        AMCAX::TopoEdge edge_shape;
        ConvertCoedge(c, edge_shape, materials);
        if (!edge_shape.IsNull())
        {
            edges.push_back(edge_shape);
        }
    }
    if (edges.size() != 0)
    {
        AMCAX::ShapeAnalysisFreeBounds::ConnectEdgesToWires(edges, 0.01, false, wires);
        parentShape = wires[0];
    }
}

void ConvertSubshell(ENTITY *entity, AMCAX::TopoShape &parentShape, std::unordered_map<AMCAX::TopoShape, InterOpMaterial> &materials) 
{
    PrintPersistentId(entity); 
    SUBSHELL *subshell = static_cast<SUBSHELL *>(entity);
    SUBSHELL *subshellChild = subshell->child();
    FACE *face = subshell->face_list();
    auto *wire = subshell->wire_list();
    if(subshellChild)
    {
        while(subshellChild)
        {
            ConvertSubshell(subshellChild, parentShape, materials);
            subshellChild = subshellChild->sibling();
        }
    }
    else if(face)
    {
        while(face->next_in_list()!=NULL)
        {
            ConvertFace(face, parentShape, materials);
            face = face->next_in_list();
        }
    }else if(wire)
    {
        while(wire->next_in_list()!=NULL)
        {
            ConvertWire(wire, parentShape, materials);
            wire = wire->next_in_list();
        }
    }
}

void ConvertLoop(ENTITY *entity, std::vector<AMCAX::TopoShape> &parentShape, std::unordered_map<AMCAX::TopoShape, InterOpMaterial> &materials,bool faceSense) 
{
    PrintPersistentId(entity); 
    if (!entity)
    {
        LOG_ERROR("entity is null");
        return;
    }
    if (!is_LOOP(entity))
    {
        LOG_WARN("entity is not loops");
        return;
    }
    ENTITY_LIST child_list;
    api_get_coedges(entity, child_list);
    std::vector<AMCAX::TopoShape> edges;
    std::vector<AMCAX::TopoShape> wires;
    for (auto c : child_list)
    {
        AMCAX::TopoEdge edge_shape;
        ConvertCoedge(c, edge_shape, materials,faceSense);
        if (!edge_shape.IsNull())
        {
            edges.push_back(edge_shape);
        }
            
    }
    if (edges.size() != 0)
    {
        AMCAX::ShapeAnalysisFreeBounds::ConnectEdgesToWires(edges, 1e-2, false, wires);
        for(auto w:wires){
            
            // if (w.Closed()) {
            //     continue;
            // }
            std::vector<AMCAX::TopoEdge> checkEdges ;
            int checkEdgeSize = 0 ;
            for (AMCAX::WireExplorer explorer(TopoCast::Wire(w)); explorer.More(); explorer.Next()) {
                // NOTE: 仅仅检查有两条边的wire
                if (checkEdgeSize > 2) {
                    break;
                }
                checkEdges.push_back(explorer.Current()) ;
                checkEdgeSize++ ;
            }
            // NOTE: 仅仅检查有两条边的wire
            if (2 == checkEdgeSize) {
                if (AMCAXTopoTools::CheckEdgeCoincide(checkEdges[0],checkEdges[1],1e-2)) {
                    continue ;
                }    
            }
            parentShape.push_back(w);
        }
       
    }
}

void ConvertCoedge(ENTITY *entity, AMCAX::TopoShape &parentShape, std::unordered_map<AMCAX::TopoShape, InterOpMaterial> &materials,bool faceSense)
{
    PrintPersistentId(entity); 
    if (!entity)
    {
        LOG_ERROR("entity is null");
        return;
    }
    if (!is_COEDGE(entity))
    {
        LOG_WARN("entity is not COEDGE");
        return;
    }
    COEDGE *coedge = dynamic_cast<COEDGE *>(entity);

    if (!coedge)
    {
        LOG_ERROR("entity is not coedge");
        return;
    }

    EDGE *edge = coedge->edge();
    
    if (!edge)
    {
        LOG_ERROR("edge is null");
        return;
    }
    bool coedgeReverse = coedge->sense() == REVERSED;
    bool edgeReverse = edge->sense()==REVERSED;

    ConvertEdge(edge, parentShape , materials ,coedgeReverse ^ faceSense);
}

void ConvertEdge(ENTITY *entity, AMCAX::TopoShape &parentShape, std::unordered_map<AMCAX::TopoShape, InterOpMaterial> &materials,bool coedgeReverse) 
{
    PrintPersistentId(entity); 
    if (!entity)
    {
        LOG_WARN("entity is null");
        return;
    }
    if (!is_EDGE(entity))
    {
        LOG_WARN("entity is not EDGE");
        return;
    }

    EDGE *edge = static_cast<EDGE *>(entity);
    if (!edge->geometry())
    {
        LOG_WARN("edge curve is null");
        return;
    }

    parentShape = ReaderTopoConvertHelper::ConvertEdge(edge,coedgeReverse,edge->sense()==REVERSED);
}


}

AMCAX_EXCHANGE_NAMESPACE_END